/**
 * Title: Hyperbolic engine
 * Description: jFamilyTree Engine
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 * Update: Mon Mar 10 00:00:00 CEST 2008
 */
package jFamilyTree.view;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import jFamilyTree.core.Complex;
import jFamilyTree.core.HyperLocation;
import jFamilyTree.core.HyperTransform;
import jFamilyTree.core.HyperTranslation;
import jFamilyTree.core.Transformer;
import jFamilyTree.core.XMath;

/**
 * Animation implements sequence of transforms in animation of tree
 * 
 * @author Bernard Bou
 */
public class Animation
{
	/**
	 * Sequence of transforms
	 */
	public List<HyperTransform> theTransforms;

	/**
	 * Behaviour
	 */
	static private final boolean finalTransformOnly = false;

	/**
	 * Constructor
	 * 
	 * @param theseTransforms
	 *        list of transforms
	 */
	protected Animation(List<HyperTransform> theseTransforms)
	{
		theTransforms = theseTransforms;
	}

	/**
	 * Constructor
	 * 
	 * @param thisFrom
	 *        source point
	 * @param thisTo
	 *        target point
	 * @param thisTransformer
	 *        transform generator
	 * @param thisOrientation
	 *        orientation
	 * @param theseSteps
	 *        number of steps
	 */
	static public Animation make(Complex thisFrom, Complex thisTo, Transformer thisTransformer, Complex thisOrientation, int theseSteps)
	{
		List<HyperTransform> theseTransforms = finalTransformOnly ? makeTransform1(thisFrom, thisTo, thisTransformer, thisOrientation) : makeTransforms(thisFrom, thisTo, thisTransformer, thisOrientation, theseSteps);
		return new Animation(theseTransforms);
	}

	/**
	 * Make sequence of transforms
	 * 
	 * @param thisFrom
	 *        source point
	 * @param thisTo
	 *        target point
	 * @param thisTransformer
	 *        transform generator
	 * @param thisOrientation
	 *        orientation
	 * @param theseSteps
	 *        number of steps
	 * @return transforms or null
	 */
	static private List<HyperTransform> makeTransforms(Complex thisFrom, Complex thisTo, Transformer thisTransformer, Complex thisOrientation, int theseSteps)
	{
		HyperTransform thisCurrentTransform = thisTransformer.getTransform();
		double thisHDist = HyperLocation.getHyperDistance(thisFrom, thisTo);
		if (thisHDist != 0.)
		{
			// steps
			if (theseSteps == 0)
			{
				theseSteps = (int) (thisHDist * 3);
			}

			// vector
			List<HyperTransform> theseTransforms = new ArrayList<HyperTransform>(theseSteps);

			// final transform
			HyperTransform thisFinalTransform = thisTransformer.makeTransform(thisFrom, thisTo, thisOrientation);
			HyperTransform thisFinalTransformInverse = new HyperTransform(thisFinalTransform).inverse();

			// this point will eventually transform to (0,0)
			Complex z0 = thisFinalTransformInverse.map(new Complex(Complex.ZERO));
			HyperTranslation thisXlat = new HyperTranslation(z0);

			// distance = arc diameter
			double thisDist = thisXlat.mag();
			// double thisRadius = thisDist / 2.;

			// normalize z0
			Complex theta = new Complex(z0).divide(thisDist);

			// iterate and make middle transforms
			for (int i = 1; i < theseSteps; ++i)
			{
				// middle transform
				HyperTransform thisiTransform;
				if (thisDist != 0.)
				{
					// progress 1/n 2/n ... i/n ... n/n
					double thisProgress = (double) i / (double) theseSteps;

					// i distance
					double thisiDist = XMath.h2e(XMath.e2h(thisDist) * thisProgress);

					// linear
					Complex z = new Complex(thisiDist, 0.);

					// rotate z by theta
					z.mul(theta);

					// make z->0 transform
					thisiTransform = thisTransformer.makeTransform(z, Complex.ZERO, thisOrientation);
				}
				else
				{
					thisiTransform = thisFinalTransform;
				}

				// add to sequence
				theseTransforms.add(new HyperTransform(thisCurrentTransform).compose(thisiTransform));
			}

			// last in sequence
			theseTransforms.add(new HyperTransform(thisCurrentTransform).compose(thisFinalTransform));
			return theseTransforms;
		}
		return null;
	}

	/**
	 * Make animation with one transform only
	 * 
	 * @param thisFrom
	 *        source point
	 * @param thisTo
	 *        source point
	 * @param thisTransformer
	 *        transform generator
	 * @param thisOrientation
	 *        orientation
	 * @return sequence of one transform
	 */
	static private Vector<HyperTransform> makeTransform1(Complex thisFrom, Complex thisTo, Transformer thisTransformer, Complex thisOrientation)
	{
		HyperTransform thisCurrentTransform = thisTransformer.getTransform();

		// final transform
		HyperTransform thisFinalTransform = thisTransformer.makeTransform(thisFrom, thisTo, thisOrientation);

		// vector
		Vector<HyperTransform> theseTransforms = new Vector<HyperTransform>(1);
		theseTransforms.addElement(new HyperTransform(thisCurrentTransform).compose(thisFinalTransform));
		return theseTransforms;
	}
}
